home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 June / Macworld (1999-06).dmg / Shareware World / Info / For Developers / MacZoop2.0.sea / MacZoop2.0 / Required Classes / CursorUtilities.cpp < prev    next >
Text File  |  1999-01-30  |  14KB  |  540 lines

  1. /*************************************************************************************************
  2. *
  3. *
  4. *        CursorUtilities.c            - cursor stuff- spinning watch, etc
  5. *
  6. *        26/9/94        ©1994, Graham Cox
  7. *
  8. *        This code based on "Macintosh Programming Secrets" 2nd Edition by Keith Rollin & Scott K
  9. *
  10. *************************************************************************************************/
  11.  
  12. #include    "CursorUtilities.h"
  13.  
  14. #include    <LowMem.h>
  15. #include    <QDOffscreen.h>
  16.  
  17. // globals:
  18.  
  19. static short            gVBLCount;
  20. static VBLTaskWithA5Ptr    gCursorTask = NULL;
  21. static acurHdl            gWatchSpinner = NULL;
  22. static acurHdl            gBallSpinner = NULL;
  23. static acurHdl            gBusyArrowSpinner = NULL;
  24. static CCrsrHandle        gColourCursor = NULL;
  25. static Boolean            gCursorPaused = FALSE;
  26. static long                gPauseTicks;
  27. static Point            gMouseTrack;
  28.  
  29. short                    gLastCursorID = -1;
  30.  
  31. // macro for doing dodgy 68000 stuff!
  32.  
  33. #ifndef __POWERPC__
  34.     VBLTaskWithA5Ptr    GetVBLRec(void) = 0x2008;
  35. #endif
  36.  
  37. static Handle    GetDetachedResource( ResType aType, short anID );
  38.  
  39. /*-------------------------***  GETDETACHEDRESOURCE  ***--------------------------------*/
  40. /*    
  41.  
  42. returns a detched resourse with type and ID passed.
  43.  
  44. ----------------------------------------------------------------------------------------*/
  45.  
  46. static Handle    GetDetachedResource( ResType aType, short anID )
  47. {
  48.     Handle    temp = NULL;
  49.     
  50.     temp = GetResource( aType, anID );
  51.     
  52.     if ( temp )
  53.         DetachResource( temp );
  54.  
  55.     return temp;
  56. }
  57.  
  58. /*--------------------------***  INITANIMATEDCURSOR  ***--------------------------------*/
  59. /*    
  60.  
  61. sets up an animated cursor by reading the 'acur' resource with the ID passed.
  62.  
  63. ----------------------------------------------------------------------------------------*/
  64.  
  65. acurHdl        InitAnimatedCursor( short resID )
  66. {
  67.     acurHdl        temp;
  68.     short        ccount;
  69.     CursHandle    aCursor;
  70.     CursHandle*    workPtr;
  71.     
  72.     temp = (acurHdl) GetDetachedResource( 'acur', resID );
  73.     
  74.     if (temp)
  75.     {
  76.         HNoPurge((Handle) temp );
  77.         
  78.         ccount = (*temp)->numCursors;
  79.         (*temp)->numCursors *= 32;
  80.         (*temp)->index = 0;
  81.         
  82.         HLock((Handle) temp);
  83.         workPtr = (*temp)->cursors;
  84.         
  85.         while( ccount-- )
  86.         {
  87.             aCursor = (CursHandle) GetDetachedResource( 'CURS', *(short*) workPtr );
  88.             HNoPurge((Handle) aCursor );
  89.             *workPtr++ = aCursor; 
  90.         }    
  91.         HUnlock((Handle) temp );
  92.     }
  93.     
  94.     return temp;
  95. }
  96.  
  97.  
  98.  
  99. /*------------------------***  DISPOSEANIMATEDCURSOR  ***-------------------------------*/
  100. /*    
  101.  
  102. disposes of an animated cursor
  103. ----------------------------------------------------------------------------------------*/
  104.  
  105. void        DisposeAnimatedCursor( acurHdl cursH )
  106. {
  107.     short ccount, mc;
  108.     
  109.     mc = (*cursH)->numCursors / 32;
  110.     
  111.     for( ccount = 0; ccount < mc; ccount++ )
  112.         DisposeHandle((Handle) (*cursH)->cursors[ ccount ] );
  113.  
  114.     DisposeHandle((Handle) cursH );
  115. }
  116.  
  117.  
  118.  
  119. /*-------------------------***  STARTCURSORANIMATION  ***-------------------------------*/
  120. /*    
  121. sets up the VBL queue to animate the cursor passed.
  122.  
  123. ----------------------------------------------------------------------------------------*/
  124.  
  125. void        StartCursorAnimation( short period, acurHdl theCursor )
  126. {
  127.     // primes the VBL queue to spin the cursor automatically
  128.     
  129.     VBLUPP        vTaskUPP;
  130.     
  131.     if (theCursor)
  132.     {
  133.         LockCursorData(theCursor);
  134.     
  135.         gVBLCount = period;
  136.         gCursorTask = (VBLTaskWithA5Ptr) NewPtr( sizeof( VBLTaskWithA5 ));
  137.         
  138.         gCursorTask->theTask.qType = vType;
  139.         
  140.         vTaskUPP = NewVBLProc((ProcPtr) VBLCursorSpin );
  141.         
  142.         gCursorTask->theTask.vblAddr = vTaskUPP;
  143.         gCursorTask->theTask.vblCount = period;
  144.         gCursorTask->theTask.vblPhase = 0;
  145.         gCursorTask->A5 = (long) LMGetCurrentA5();
  146.         gCursorTask->theCursor = theCursor;
  147.         
  148.         (void) VInstall((QElemPtr) gCursorTask );
  149.         
  150.         gCursorPaused = FALSE;
  151.     }
  152. }
  153.  
  154.  
  155. /*-------------------------***  STOPCURSORANIMATION  ***--------------------------------*/
  156. /*    
  157.  
  158. Kills the current cursor in the VBL queue and unlocks it. This then restores the arrow.
  159. ----------------------------------------------------------------------------------------*/
  160.  
  161. void        StopCursorAnimation()
  162. {
  163.     // removes spinning cursor task from the VBL queue
  164.     
  165.     acurHdl        theCursor;
  166.     
  167.     if ( gCursorTask )
  168.     {
  169.         (void) VRemove((QElemPtr) gCursorTask );
  170.         
  171.         theCursor = gCursorTask->theCursor;
  172.         DisposeRoutineDescriptor( gCursorTask->theTask.vblAddr );
  173.         
  174.         DisposePtr((Ptr) gCursorTask);
  175.         gCursorTask = NULL;
  176.         UnlockCursorData( theCursor );
  177.         
  178.         // force switch to default cursor:
  179.         
  180.         gLastCursorID = -1;
  181.         SetCursorShape( 0 );
  182.     }
  183. }
  184.  
  185.  
  186. /*------------------------***  PAUSECURSORANIMATION  ***--------------------------------*/
  187. /*    
  188.  
  189. Pauses the current animating cursor, returning the cursor to the shape passed. If the
  190. cursor is not animating, this does nothing. The animation can be restarted with a call
  191. to ResumeCursorAnimation().
  192. ----------------------------------------------------------------------------------------*/
  193.  
  194. void        PauseCursorAnimation( short tempFixedCursorID )
  195. {
  196.     if ( CursorAnimating())
  197.     {
  198.         gCursorPaused = TRUE;
  199.         gLastCursorID = -1;
  200.         gPauseTicks = 0;
  201.     }
  202.     SetCursorShape( tempFixedCursorID );
  203. }
  204.  
  205.  
  206.  
  207. /*------------------------***  RESUMECURSORANIMATION  ***-------------------------------*/
  208. /*    
  209. Restarts a paused animating cursor, if there is one, otherwise does nothing but reset the
  210. flag.
  211. ----------------------------------------------------------------------------------------*/
  212.  
  213. void        ResumeCursorAnimation()
  214. {
  215.     gCursorPaused = FALSE;
  216. }
  217.  
  218.  
  219. /*-----------------------------***  ANIMATECURSOR  ***----------------------------------*/
  220. /*    
  221.  
  222. Can be called repeatedly to animate a cursor. However, it is simpler to let the VBL task
  223. do this in general.
  224.  
  225. ----------------------------------------------------------------------------------------*/
  226.  
  227. void        AnimateCursor( short increment, acurHdl theCursor )
  228. {
  229.     // used only for non-VBL cursor spinning
  230.     
  231.     short        oldIndex,newIndex;
  232.     CursHandle    aCursor;
  233.     
  234.     if ( theCursor && !gCursorPaused )
  235.     {
  236.         oldIndex = (*theCursor)->index / 32;
  237.         
  238.         (*theCursor)->index += increment;
  239.         (*theCursor)->index %= (*theCursor)->numCursors;
  240.         
  241.         newIndex = (*theCursor)->index / 32;
  242.         
  243.         if (newIndex != oldIndex)
  244.         {
  245.             aCursor = (*theCursor)->cursors[newIndex];
  246.             SetCursor(*aCursor);
  247.         }
  248.     }
  249. }
  250.  
  251.  
  252. /*----------------------------***  LOCKCURSORDATA  ***----------------------------------*/
  253. /*    
  254.  
  255. locks down the cursor handles ready for animation
  256.  
  257. ----------------------------------------------------------------------------------------*/
  258.  
  259. void        LockCursorData( acurHdl theCursor )
  260. {
  261.     // locks the cursor and its internal data
  262.     
  263.     short        ccount;
  264.     CursHandle    *workPtr;
  265.     
  266.     ccount = (*theCursor)->numCursors / 32;
  267.     
  268.     HLockHi((Handle) theCursor);
  269.     workPtr = (*theCursor)->cursors;
  270.     
  271.     while (ccount--)
  272.         HLockHi((Handle) *workPtr++);    
  273. }
  274.  
  275.  
  276. /*--------------------------***  UNLOCKCURSORDATA  ***----------------------------------*/
  277. /*    
  278.  
  279. unlocks the cursor handles
  280. ----------------------------------------------------------------------------------------*/
  281.  
  282. void        UnlockCursorData( acurHdl theCursor )
  283. {
  284.     // unlocks the cursor
  285.     
  286.     short        ccount;
  287.     CursHandle    *workPtr;
  288.     
  289.     ccount = (*theCursor)->numCursors / 32;
  290.     workPtr = (*theCursor)->cursors;
  291.     
  292.     while (ccount--)
  293.         HUnlock((Handle) *workPtr++);    
  294.  
  295.     HUnlock((Handle) theCursor);
  296. }
  297.  
  298. /*-----------------------------***  VBLCURSORSPIN  ***----------------------------------*/
  299. /*    
  300.  
  301. this is the VBL callback that actually performs the animation of a VBL-driven cursor
  302. ----------------------------------------------------------------------------------------*/
  303.  
  304. #ifndef __POWERPC__
  305.     void        VBLCursorSpin()
  306. #else
  307.     void        VBLCursorSpin(VBLTaskWithA5Ptr theTask)
  308. #endif
  309. {
  310.     // this is the function that is called from the interrupt task. 
  311.     
  312.     acurHdl        theCrsr;
  313.     long        oldA5;
  314.     
  315. #ifndef __POWERPC__
  316.     VBLTaskWithA5Ptr    theTask;
  317.     
  318.     theTask = GetVBLRec();
  319.     oldA5 = SetA5(theTask->A5);
  320.     if (LMGetCrsrBusy() == 0)
  321.     {
  322.         theCrsr = theTask->theCursor;
  323.         AnimateCursor( 32, theCrsr );
  324.     }
  325.     theTask->theTask.vblCount = gVBLCount;
  326.     (void) SetA5( oldA5 );
  327.     
  328. #else
  329.     oldA5 = SetA5( theTask->A5 );
  330.         
  331.     if (LMGetCrsrBusy() == 0)
  332.     {
  333.         theCrsr = theTask->theCursor;
  334.         AnimateCursor( 32, theCrsr );
  335.     }
  336.     theTask->theTask.vblCount = gVBLCount;
  337.     (void) SetA5( oldA5 );
  338. #endif
  339. }
  340.  
  341.  
  342. /*-----------------------------***  SETWATCHCURSOR  ***---------------------------------*/
  343. /*    
  344.  
  345. starts the animated watch cursor. To stop it, call StopCursorAnimation.
  346. ----------------------------------------------------------------------------------------*/
  347.  
  348. void        SetWatchCursor()
  349. {
  350.     // HL call installs VBL spinner for watch, which should have been inited beforehand
  351.     
  352.     if ( gCursorTask )
  353.         StopCursorAnimation();
  354.     
  355.     if ( gWatchSpinner && ! gCursorTask )
  356.         StartCursorAnimation( 8, gWatchSpinner );
  357. }
  358.  
  359. /*---------------------------***  SETBEACHBALLCURSOR  ***-------------------------------*/
  360. /*    
  361.  
  362. starts the animated "beachball" cursor. To stop it, call StopCursorAnimation.
  363. ----------------------------------------------------------------------------------------*/
  364.  
  365. void        SetBeachBallCursor()
  366. {
  367.     if ( gCursorTask )
  368.         StopCursorAnimation();
  369.     
  370.     if ( gBallSpinner && ! gCursorTask )
  371.         StartCursorAnimation( 3, gBallSpinner );
  372.  
  373. }
  374.  
  375.  
  376. void        SetBusyArrowCursor()
  377. {
  378.     if ( gCursorTask )
  379.         StopCursorAnimation();
  380.     
  381.     if ( gBallSpinner && ! gCursorTask )
  382.         StartCursorAnimation( 3, gBusyArrowSpinner );
  383. }
  384.  
  385. /*-------------------------------***  GETMODIFIERS  ***---------------------------------*/
  386. /*    
  387.  
  388. can be called at any time to get the current state of the keyboard modifier keys. Useful
  389. if the cursor is changed with different modifiers down.
  390. ----------------------------------------------------------------------------------------*/
  391.  
  392. short        GetModifiers()
  393. {
  394.     // returns the current state of the modifier keys as if it was the modifier field in
  395.     // an event record. This can be called at any time, even when an event is not available.
  396.     // It is very useful for updating the cursor to show the modifier state.
  397.     
  398.     short            modifiers = 0;
  399.     unsigned char    theKeys[16];
  400.     KeyMap*            pKeys;
  401.     
  402.     pKeys = (KeyMap*) theKeys;
  403.     
  404.     GetKeys( *pKeys );
  405.     
  406.     ((theKeys[0x37 >> 3] >> (0x37 & 7)) & 1)? modifiers |= cmdKey       : 0;
  407.     ((theKeys[0x38 >> 3] >> (0x38 & 7)) & 1)? modifiers |= shiftKey   : 0;
  408.     ((theKeys[0x39 >> 3] >> (0x39 & 7)) & 1)? modifiers |= alphaLock  : 0;
  409.     ((theKeys[0x3A >> 3] >> (0x3A & 7)) & 1)? modifiers |= optionKey  : 0;
  410.     ((theKeys[0x3B >> 3] >> (0x3B & 7)) & 1)? modifiers |= controlKey : 0;
  411.  
  412.     (! Button())? modifiers |= btnState : 0;
  413.     
  414.     return modifiers;
  415. }
  416.  
  417.  
  418. /*------------------------------***  APPCURSORINIT  ***---------------------------------*/
  419. /*    
  420. high-level call to initialise the animated cursors. Extend this for others you may have
  421. ----------------------------------------------------------------------------------------*/
  422.  
  423. void        AppCursorInit()
  424. {
  425.     // called once at application start-up to initialise the animated cursor globals.
  426.  
  427.     gWatchSpinner = InitAnimatedCursor( kWatchResID );
  428.     gBallSpinner = InitAnimatedCursor( kBeachBallResID );
  429.     gBusyArrowSpinner = InitAnimatedCursor( kBusyArrowResID );
  430.     
  431.     gLastCursorID = -1;
  432.     gColourCursor = NULL;
  433. }
  434.  
  435.  
  436. /*------------------------------***  APPCURSORFREE  ***---------------------------------*/
  437. /*    
  438. high-level call to delete all animated cursors
  439. ----------------------------------------------------------------------------------------*/
  440.  
  441. void        AppCursorFree()
  442. {
  443.     StopCursorAnimation();
  444.     
  445.     DisposeAnimatedCursor( gWatchSpinner );
  446.     DisposeAnimatedCursor( gBallSpinner );
  447.     DisposeAnimatedCursor( gBusyArrowSpinner );
  448. }
  449.  
  450. /*------------------------------***  CURSORANIMATING  ***-------------------------------*/
  451. /*    
  452. returns TRUE if an animated cursor is on screen. You should not call
  453. SetCursor if this is true, or ugly cursor flickering occurs.
  454. ----------------------------------------------------------------------------------------*/
  455.  
  456. Boolean        CursorAnimating()
  457. {
  458.     return (( gCursorTask != NULL ) && !gCursorPaused );
  459. }
  460.  
  461.  
  462.  
  463. Boolean        CursorAnimationVBLRunning()
  464. {
  465.     return ( gCursorTask != NULL );
  466. }
  467.  
  468.  
  469. /*-------------------------------***  SETCURSORSHAPE  ***-------------------------------*/
  470. /*
  471. You can use this instead of GetCursor/SetCursor to set the cursor shape. This also will
  472. correctly manage a colour cursor if there is one available with the same ID. This just
  473. works without any special coding, optimising for loading the colour cursor minimally.
  474. If you pass 0 it will set the arrow. This method is unsuitable for VBL animated cursors.    
  475. ----------------------------------------------------------------------------------------*/
  476.  
  477. void        SetCursorShape( short resID )
  478. {
  479.     if (( resID != gLastCursorID ) && ! CursorAnimating())
  480.     {
  481.         CCrsrHandle        ncH = NULL;
  482.             
  483.         gLastCursorID = resID;
  484.             
  485.         if ( resID == 0 )
  486.             SetCursor( &qd.arrow );
  487.         else
  488.         {
  489.             // see if there's a colour cursor with that ID. We do not look for colour
  490.             // cursors with IDs less than 128 since for some reason Inside Mac says
  491.             // this is not allowed. Can't really see why this is!
  492.             
  493.             if ( resID > 127 )
  494.             {
  495.                 ncH = GetCCursor( resID );
  496.                 
  497.                 if ( ncH )
  498.                 {
  499.                     // due to a subtle and undocumented side-effect of SetCCursor, we must be
  500.                     // careful to preserve the GDevice around this call, or bad things can
  501.                     // happen.
  502.                     
  503.                     GDHandle    saveDevice;
  504.                     
  505.                     saveDevice = GetGDevice();
  506.                     
  507.                     SetCCursor( ncH );
  508.                     SetGDevice( saveDevice );
  509.                 }
  510.             }
  511.             
  512.             // if no colour cursor, use a black and white one. This works with all
  513.             // cursor IDs, including the standard iBeamCursor, crossCursor, etc.
  514.             
  515.             if ( ncH == NULL )
  516.             {
  517.                 CursHandle    cH;
  518.                 
  519.                 cH = GetCursor( resID );
  520.         
  521.                 if ( cH )
  522.                 {
  523.                     HLockHi((Handle) cH );
  524.                     SetCursor( *cH );
  525.                     HUnlock((Handle) cH );
  526.                 }
  527.             }
  528.         }    
  529.         
  530.         // dispose any old colour cursor
  531.         
  532.         if ( gColourCursor )
  533.             DisposeCCursor( gColourCursor );
  534.         
  535.         // keep track of new one or NULL
  536.         
  537.         gColourCursor = ncH;
  538.     }
  539. }
  540.